/**
 * @file heart_rate.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_watch.h"

#if USE_LV_WATCH_HEART_RATE != 0

#include <stdio.h>

/*********************
 *      DEFINES
 *********************/
#define HEART_RATE_TEST 1
#define HEART_RATE_W 240
#define HEART_RATE_H 100
#define HEART_RATE_Y_BASE (LV_VER_RES/3)
#define HEART_RATE_UPDATE_PERIOD (1000 * (24 * 3600) / HEART_RATE_W)

/**********************
 *      TYPEDEFS
 **********************/
typedef struct {
    lv_cont_ext_t oldext;
    lv_obj_t * heart_rate_canvas;
    lv_obj_t * current_rate;
    lv_obj_t * current_img;
    lv_obj_t * highest_rate;
    lv_obj_t * lowest_rate;
    lv_obj_t * highest_dot;
    lv_obj_t * lowest_dot;
    lv_task_t * update_task;
} heart_rate_ext_t;

typedef struct {
    uint16_t current_x;
    uint16_t highest_x;
    uint16_t lowest_x;
} heart_rate_mng_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void heart_img_update(void * obj, lv_anim_value_t value);

/**********************
 *  STATIC VARIABLES
 **********************/
uint8_t heart_rate_data[HEART_RATE_W];
static heart_rate_mng_t hr_mng = {};

/**********************
 *      MACROS
 **********************/
LV_IMG_DECLARE(icon_heart_rate_heart)
LV_IMG_DECLARE(icon_heart_rate_highest)
LV_IMG_DECLARE(icon_heart_rate_lowest)
LV_IMG_DECLARE(icon_heart_rate_highest_dot)
LV_IMG_DECLARE(icon_heart_rate_lowest_dot)

/**********************
 *   STATIC FUNCTIONS
 **********************/
static void heart_rate_test_init(void)
{
    uint32_t i, j;
    int8_t z;

    j = 70;

    for(i = 0; i < HEART_RATE_W - 230; i++) {
        if(j == 70) z = 1;
        if(j == 90) z = -1;

        j += z;
        heart_rate_data[i] = j;
    }
    for(; i < HEART_RATE_W - 200; i++) {
        heart_rate_data[i] = 0;
    }

    j = 70;
    for(; i < HEART_RATE_W; i++) {
        if(j == 70) z = 1;
        if(j == 90) z = -1;

        j += z;
        heart_rate_data[i] = j;
    }

}

static uint8_t heart_rate_get_data(uint32_t x)
{
    return (HEART_RATE_H * heart_rate_data[x]) / LV_VER_RES;
}

static void heart_set_local_line_style(lv_obj_t * obj)
{
    lv_obj_set_style_local_line_color(obj, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_obj_set_style_local_line_width(obj, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, 1);
    lv_obj_set_style_local_line_opa(obj, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
}

static void heart_rate_add_record(lv_obj_t * canvas, uint32_t x, lv_color_t start, lv_color_t end)
{
    uint32_t i, h, h_before, offset = 1;
    uint32_t mix;
    lv_color_t act_color;

    h = heart_rate_get_data(x);
    heart_rate_ext_t * ext = lv_obj_get_ext_attr(lv_obj_get_parent(canvas));

    uint32_t pos = (LV_BIDI_DIR_RTL == lv_obj_get_base_dir(canvas)) ? (HEART_RATE_W - x - 1) : x;

    if(heart_rate_data[x] >= heart_rate_data[hr_mng.highest_x]) {
        hr_mng.highest_x = x;
        lv_obj_align(ext->highest_dot, canvas, LV_ALIGN_IN_TOP_LEFT, pos - lv_obj_get_width(ext->highest_dot) / 2,
                     HEART_RATE_H - h - lv_obj_get_width(ext->highest_dot) / 2);
    }

    if((heart_rate_data[x] <= heart_rate_data[hr_mng.lowest_x]) && (heart_rate_data[x] != 0)) {
        hr_mng.lowest_x = x;
        lv_obj_align(ext->lowest_dot, canvas, LV_ALIGN_IN_TOP_LEFT, pos - lv_obj_get_width(ext->lowest_dot) / 2,
                     HEART_RATE_H - h - lv_obj_get_width(ext->lowest_dot) / 2);
    }

    if(x != 0) {
        h_before = heart_rate_get_data(x - 1);
        if(h_before == h)
            offset = 1;
        else if(h_before < h)
            offset = h - h_before;
        else
            offset = h_before - h;
    } else {
        h_before = h;
    }

    if(h_before <= h) {
        for(i = HEART_RATE_H - 1; i > HEART_RATE_H - h + offset; i--) {
            mix = ((h - (HEART_RATE_H - i)) * 255) / h;
            act_color = lv_color_mix(start, end, mix);
            lv_canvas_set_px(canvas, pos, i, act_color);
        }

        for(; i > HEART_RATE_H - h; i--) {
            lv_canvas_set_px(canvas, pos, i, LV_COLOR_RED);
        }
    } else {
        for(i = HEART_RATE_H - 1; i > HEART_RATE_H - h; i--) {
            mix = ((h - (HEART_RATE_H - i)) * 255) / h;
            act_color = lv_color_mix(start, end, mix);
            lv_canvas_set_px(canvas, pos, i, act_color);
        }

        for(; i > HEART_RATE_H - h - offset; i--) {
            lv_canvas_set_px(canvas, pos, i, LV_COLOR_RED);
        }
    }

    if(heart_rate_data[x])
        lv_anim_set_time(lv_anim_get(ext->current_img, heart_img_update), 60 * 1000 / heart_rate_data[x]);
    else
        lv_anim_set_time(lv_anim_get(ext->current_img, heart_img_update), 0xffffffff);
}

static lv_obj_t * heart_rate_add_line(lv_obj_t * canvas, lv_point_t * points, uint32_t y, char * text)
{
    y = (HEART_RATE_H * y) / LV_VER_RES;

    points[0].x = 0;
    points[1].x = HEART_RATE_W - 1;
    points[0].y = HEART_RATE_H - y;
    points[1].y = HEART_RATE_H - y;

    lv_obj_t * line = lv_line_create(canvas, NULL);
    heart_set_local_line_style(line);
    lv_line_set_points(line, points, 2);

    if(text) {
        lv_obj_t * label = lv_label_create(line, NULL);
        lv_label_set_text(label, text);
        lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_FONT_12);
        lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
        if(lv_obj_get_base_dir(canvas) == LV_BIDI_DIR_RTL)
            lv_obj_align(label, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 5, 0);
        else
            lv_obj_align(label, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -5, 0);
    }

    return line;
}

static lv_obj_t * heart_rate_add_dot(lv_obj_t * par, lv_obj_t * align_obj, uint16_t x, char * text)
{
    lv_obj_t * dot = lv_obj_create(par, NULL);
    uint8_t posx = x;
    lv_obj_set_size(dot, 1, 2);
    if(lv_obj_get_base_dir(par) == LV_BIDI_DIR_RTL)
        posx = HEART_RATE_W - x - 1;
    lv_obj_align(dot, align_obj, LV_ALIGN_OUT_TOP_LEFT, posx, 0);

    if(text) {
        lv_obj_t * label = lv_label_create(par, NULL);
        lv_label_set_text(label, text);
        lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_FONT_12);
        lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
        lv_obj_align(label, dot, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
        lv_obj_set_base_dir(label, LV_BIDI_DIR_LTR);
    }

    return dot;
}

static void heart_rate_add_axis_x(lv_obj_t * par, lv_obj_t * align_obj)
{
    lv_obj_t * axis_x = lv_obj_create(par, NULL);
    lv_obj_set_style_local_border_width(axis_x, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 1);
    lv_obj_set_style_local_border_color(axis_x, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_obj_set_style_local_border_opa(axis_x, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_50);
    lv_obj_set_size(axis_x, HEART_RATE_W, 1);
    lv_obj_align(axis_x, align_obj, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);

    heart_rate_add_dot(par, axis_x, 30, NULL);
    heart_rate_add_dot(par, axis_x, 60, "6:00");
    heart_rate_add_dot(par, axis_x, 90, NULL);
    heart_rate_add_dot(par, axis_x, 120, "12:00");
    heart_rate_add_dot(par, axis_x, 150, NULL);
    heart_rate_add_dot(par, axis_x, 180, "18:00");
    heart_rate_add_dot(par, axis_x, 210, NULL);
}

static void heart_rate_add_axis_y(lv_obj_t * par)
{
    //add break 50
    static lv_point_t points_50[2];
    heart_rate_add_line(par, points_50, 50, "50");

    //add break 100
    static lv_point_t points_100[2];
    heart_rate_add_line(par, points_100, 100, "100");

    //add break 150
    static lv_point_t points_150[2];
    heart_rate_add_line(par, points_150, 150, "150");

    //add break 200
    static lv_point_t points_200[2];
    heart_rate_add_line(par, points_200, 200, "200");
}

static void heart_rate_update_display(lv_obj_t * obj, uint32_t x)
{
    char str[20] = {};
    heart_rate_ext_t * ext = lv_obj_get_ext_attr(obj);
    lv_obj_t * heart_rate_canvas = ext->heart_rate_canvas;
    heart_rate_add_record(heart_rate_canvas, x, LV_COLOR_BLACK, LV_COLOR_MAKE(0xB0, 0x00, 0x00));
    lv_obj_invalidate(heart_rate_canvas);

    snprintf(str, 20, "%d %s", heart_rate_data[x], (char *)lv_lang_get_text(WATCH_TEXT_ID_BPM));
    lv_label_set_text(ext->current_rate, str);

    memset(str, 0, 20);
    snprintf(str, 20, "%d", heart_rate_data[hr_mng.highest_x]);
    lv_label_set_text(ext->highest_rate, str);

    memset(str, 0, 20);
    snprintf(str, 20, "%d", heart_rate_data[hr_mng.lowest_x]);
    lv_label_set_text(ext->lowest_rate, str);
}

static void heart_rate_update_task(lv_task_t * task)
{
    hal_rtc_t tm;
    uint32_t i, x;
    lv_obj_t * obj = task->user_data;
    heart_rate_ext_t * heart_rate_ext = lv_obj_get_ext_attr(obj);

    Hal_Rtc_Gettime(&tm);
    x = (tm.tm_hour * 60 + tm.tm_min) / (24 * 60 / HEART_RATE_W);

    printf("%s x %d, current_x %d\n", __FUNCTION__, x, hr_mng.current_x);
    if(x < hr_mng.current_x) {
        lv_canvas_ext_t * ext = lv_obj_get_ext_attr(heart_rate_ext->heart_rate_canvas);
        uint8_t color_size = lv_img_cf_get_px_size(LV_IMG_CF_TRUE_COLOR);
        uint32_t buff_size = (color_size * HEART_RATE_W * HEART_RATE_H) / 8;

        hr_mng.current_x = x;
        hr_mng.lowest_x = 0;
        hr_mng.highest_x = 0;

        _lv_memset_00((void*)ext->dsc.data, buff_size);

        for(i = 0; i < hr_mng.current_x; i++) {
            heart_rate_update_display(obj, i);
        }
    } else if(x > hr_mng.current_x) {
        for(i = hr_mng.current_x; i <= x; i++) {
            heart_rate_update_display(obj, i);
        }
        hr_mng.current_x = x;
    }
}

static void heart_rate_update_start(lv_obj_t * obj)
{
    uint16_t i;

    hal_rtc_t tm;
    Hal_Rtc_Gettime(&tm);

    hr_mng.current_x = (tm.tm_hour * 60 + tm.tm_min) / (24 * 60 / HEART_RATE_W);

    printf("currentx %d\n", hr_mng.current_x);
    for(i = 0; i < hr_mng.current_x; i++) {
        heart_rate_update_display(obj, i);
    }

    lv_task_t * update_task = lv_task_create(heart_rate_update_task, HEART_RATE_UPDATE_PERIOD, LV_TASK_PRIO_HIGH, obj);
    heart_rate_ext_t * ext = lv_obj_get_ext_attr(obj);
    ext->update_task = update_task;
}

static void heart_rate_prepare_destory(lv_obj_t * act)
{
    lv_obj_t * watch_obj = NULL;
    lv_watch_get_child_obj(act, lv_watch_obj_signal, &watch_obj);
    lv_obj_t * cont = lv_obj_get_child(watch_obj, NULL);
    heart_rate_ext_t * ext = lv_obj_get_ext_attr(cont);
    lv_canvas_ext_t * canvas_ext = NULL;
    if(ext) {
        canvas_ext = lv_obj_get_ext_attr(ext->heart_rate_canvas);
    }
    if((NULL != canvas_ext)) {
        if(NULL != canvas_ext->dsc.data)
            lv_mem_free(canvas_ext->dsc.data);
    }
    lv_task_del(ext->update_task);
}

static void heart_img_update(void * obj, lv_anim_value_t value)
{
    lv_obj_set_style_local_transform_zoom(obj, LV_BTN_PART_MAIN, LV_STATE_DEFAULT,
                                          value);
}

static lv_obj_t * heart_create_content(lv_obj_t * par)
{
    lv_obj_t * heart_rate_content = lv_cont_create(par, NULL);
    lv_obj_set_style_local_bg_color(heart_rate_content, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
    lv_obj_set_style_local_bg_grad_color(heart_rate_content, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
    lv_obj_set_size(heart_rate_content, lv_obj_get_width(par), lv_obj_get_height(par));
    lv_watch_obj_add_element(heart_rate_content);

    heart_rate_ext_t * ext = lv_obj_allocate_ext_attr(heart_rate_content, sizeof(heart_rate_ext_t));
    ext->heart_rate_canvas = NULL;
    ext->current_rate = NULL;
    ext->highest_rate = NULL;
    ext->lowest_rate = NULL;
    ext->highest_dot = NULL;
    ext->lowest_dot = NULL;

    uint8_t color_size = lv_img_cf_get_px_size(LV_IMG_CF_TRUE_COLOR);
    uint32_t buff_size = (color_size * HEART_RATE_W * HEART_RATE_H) / 8;
    uint8_t * canvas_buff = lv_mem_alloc(buff_size);
    memset(canvas_buff, 0, buff_size);

    lv_obj_t * heart_rate_canvas = lv_canvas_create(heart_rate_content, NULL);
    ext->heart_rate_canvas = heart_rate_canvas;
    lv_obj_set_size(heart_rate_canvas, HEART_RATE_W, HEART_RATE_H);
    lv_canvas_set_buffer(heart_rate_canvas, canvas_buff, HEART_RATE_W, HEART_RATE_H, LV_IMG_CF_TRUE_COLOR);
    lv_obj_align(heart_rate_canvas, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -HEART_RATE_Y_BASE);

    //add axis-x
    heart_rate_add_axis_x(heart_rate_content, heart_rate_canvas);

    //add axis-y
    heart_rate_add_axis_y(heart_rate_canvas);

    //add current rate
    lv_obj_t * current_cont = lv_cont_create(heart_rate_content, NULL);
    lv_obj_set_size(current_cont, LV_HOR_RES * 2 / 3, 30);
    lv_obj_align(current_cont, heart_rate_canvas, LV_ALIGN_OUT_TOP_MID, 0, -10);
    lv_obj_set_style_local_bg_opa(current_cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_style_local_pad_inner(current_cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10);
    lv_obj_set_style_local_pad_left(current_cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_obj_set_style_local_pad_right(current_cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_cont_set_layout(current_cont, LV_LAYOUT_ROW_MID);

    lv_obj_t * current_img = lv_img_create(current_cont, NULL);
    ext->current_img = current_img;
    lv_img_set_src(current_img, ICON_HEART_RATE_HEART);

    char str[20];
    lv_obj_t * current_rate = lv_label_create(current_cont, NULL);
    ext->current_rate = current_rate;
    lv_obj_set_style_local_text_font(current_rate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_FONT_30);
    lv_obj_set_style_local_text_color(current_rate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);

    snprintf(str, 20, "%d %s", 0, (char *)lv_lang_get_text(WATCH_TEXT_ID_BPM));
    lv_label_set_text(current_rate, str);

    lv_anim_path_t path;
    lv_anim_t a;
    lv_anim_path_init(&path);
    lv_anim_path_set_cb(&path, lv_anim_path_ease_out);
    lv_anim_init(&a);
    lv_anim_set_var(&a, current_img);
    lv_anim_set_path(&a, &path);
    lv_anim_set_time(&a, 1000);
    lv_anim_set_values(&a, LV_IMG_ZOOM_NONE * 2 / 3, LV_IMG_ZOOM_NONE);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_set_exec_cb(&a, heart_img_update);
    lv_anim_start(&a);

    //add highest rate
    lv_obj_t * highest_cont = lv_cont_create(heart_rate_content, current_cont);
    lv_obj_set_size(highest_cont, LV_HOR_RES / 4, 30);

    lv_obj_t * highest_img = lv_img_create(highest_cont, NULL);
    lv_img_set_src(highest_img, ICON_HEART_RATE_HIGHEST);

    lv_obj_t * highest_rate = lv_label_create(highest_cont, NULL);
    ext->highest_rate = highest_rate;
    lv_obj_set_style_local_text_font(highest_rate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_FONT_30);
    lv_obj_set_style_local_text_color(highest_rate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text(highest_rate, "0");

    lv_obj_t * highest_dot_img = lv_img_create(heart_rate_canvas, NULL);
    ext->highest_dot = highest_dot_img;
    lv_img_set_src(highest_dot_img, ICON_HEART_RATE_HIGHEST_DOT);

    //add lowest rate
    lv_obj_t * lowest_cont = lv_cont_create(heart_rate_content, highest_cont);

    lv_obj_t * lowest_img = lv_img_create(lowest_cont, NULL);
    lv_img_set_src(lowest_img, ICON_HEART_RATE_LOWEST);

    lv_obj_t * lowest_rate = lv_label_create(lowest_cont, NULL);
    ext->lowest_rate = lowest_rate;
    lv_obj_set_style_local_text_font(lowest_rate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_THEME_WATCH_NIGHT_FONT_30);
    lv_obj_set_style_local_text_color(lowest_rate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_label_set_text(lowest_rate, "0");

    lv_obj_t * lowest_dot_img = lv_img_create(heart_rate_canvas, NULL);
    ext->lowest_dot = lowest_dot_img;
    lv_img_set_src(lowest_dot_img, ICON_HEART_RATE_LOWEST_DOT);

    if(LV_BIDI_DIR_RTL == lv_obj_get_base_dir(heart_rate_content)) {
        lv_obj_align(lowest_cont, heart_rate_canvas, LV_ALIGN_OUT_BOTTOM_MID, -45, 20);
        lv_obj_align(highest_cont, lowest_cont, LV_ALIGN_OUT_RIGHT_MID, 20, 0);
    } else {
        lv_obj_align(highest_cont, heart_rate_canvas, LV_ALIGN_OUT_BOTTOM_MID, -45, 20);
        lv_obj_align(lowest_cont, highest_cont, LV_ALIGN_OUT_RIGHT_MID, 20, 0);
    }

    return heart_rate_content;
}


/**********************
 *   GLOBAL FUNCTIONS
***********************/
#if USE_LV_WATCH_LAUNCHER2 != 0
void heart_rate_create_tab(lv_obj_t * par)
{
    lv_obj_t * cont = heart_create_content(par);
    lv_tileview_add_element(lv_obj_get_parent(par), cont);
    lv_obj_set_click(cont, false);
#if HEART_RATE_TEST
    heart_rate_test_init();
#endif
    heart_rate_update_start(cont);
}

void heart_rate_destroy_tab(lv_obj_t * tab)
{
    lv_obj_t * cont = lv_obj_get_child(tab, NULL);
    heart_rate_ext_t * ext = lv_obj_get_ext_attr(cont);
    lv_canvas_ext_t * canvas_ext = NULL;
    if(ext) {
        canvas_ext = lv_obj_get_ext_attr(ext->heart_rate_canvas);
    }
    if((NULL != canvas_ext)) {
        if(NULL != canvas_ext->dsc.data)
            lv_mem_free(canvas_ext->dsc.data);
    }
    lv_task_del(ext->update_task);
}
#endif

lv_obj_t * heart_rate_create(lv_obj_t * activity_obj)
{
    if(activity_obj == NULL) {
        lv_watch_activity_ext_t activity_ext;
        memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
        activity_ext.actId = ACT_ID_HEART_RATE;
        activity_ext.create = heart_rate_create;
        activity_ext.prepare_destory = heart_rate_prepare_destory;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
        LV_ASSERT_MEM(activity_obj);
        if(activity_obj == NULL) return NULL;
    }

    lv_obj_t * watch_obj = lv_watch_obj_create(activity_obj);
    lv_obj_t * cont = heart_create_content(watch_obj);

#if HEART_RATE_TEST
    heart_rate_test_init();
#endif

    heart_rate_update_start(cont);

    return watch_obj;
}

void heart_rate_create_event_cb(lv_obj_t * btn, lv_event_t e)
{
    (void)btn;
    if(LV_EVENT_CLICKED == e) {
        lv_obj_t * obj = heart_rate_create(NULL);
        LV_ASSERT_MEM(obj);
    }
}
#endif /*USE_LV_WATCH_HEART_RATE*/
